/*
 * Copyright (C) 2010 Peter Schönhofen
 *
 * This file is part of the Akkad Compiler Framework.
 *
 * The Akkad Compiler Framework is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

package hu.anaxo.akkad.processor.pl0

import hu.anaxo.akkad.processor.{CustomParser, Syntax}

/**
 * PL/0 parser
 */

object PL0Parser extends CustomParser {

  // terminals

  def number: Parser[Int] =
    elem("number", _.isInstanceOf[Number]) ^^ {
      _.asInstanceOf[Number].value
    }
  def identifier: Parser[String] =
    elem("identifier", _.isInstanceOf[Identifier]) ^^ {
      _.asInstanceOf[Identifier].value
    }

  // parser rules

  private def block: Parser[Block] =
    opt(constants) ~~
    opt(variables) ~~
    rep(procedureDef) ~~
    statement ^^ {
      case constants ~ variables ~ procedures ~ statement =>
        val declarations = constants.getOrElse(List()) ++ variables.toList ++ procedures
        Block(declarations, statement)
    }

  private def constants: Parser[List[ConstantDef]] =
    !Keyword("CONST") ~~ rep1sep(constantDef, Symbol(",")) ~~ !Symbol(";")
  private def constantDef: Parser[ConstantDef] =
    identifier ~~ !Symbol("=") ~~ number ^^
      build(ConstantDef)

  private def variables: Parser[VariableDefs] =
    !Keyword("VAR") ~~ rep1sep(identifier, Symbol(",")) ~~ !Symbol(";") ^^
      build(VariableDefs)

  private def procedureDef: Parser[ProcedureDef] =
    !Keyword("PROCEDURE") ~~ identifier ~~ !Symbol(";") ~~ block ~~ !Symbol(";") ^^
      build(ProcedureDef)

  private def statement: Parser[Statement] =
    identifier ~~ !Symbol(":=") ~~ expression ^^
      build(Assign) |
    !Keyword("CALL") ~~ identifier ^^
      build(Call) |
    !Keyword("BEGIN") ~~ rep1sep(statement, Symbol(";")) ~~ !Keyword("END") ^^
      build(Statements) |
    !Keyword("IF") ~~ condition ~~ !Keyword("THEN") ~~ statement ^^
      build(If) |
    !Keyword("WHILE") ~~ condition ~~ !Keyword("DO") ~~ statement ^^
      build(While) |
    err("invalid statement")

  private def expression: Parser[Expression] =
    chainl1(signedTerm, term, Symbol("+") ^^^ (Add(_, _)) | Symbol("-") ^^^ (Subtract(_, _)))
  private def signedTerm: Parser[Expression] =
    !Symbol("-") ~~ term ^^
      build(Negate) |
    !opt(Symbol("+")) ~~ term
  private def term: Parser[Expression] =
    chainl1(factor, Symbol("*") ^^^ { Multiply(_, _) } |
                    Symbol("/") ^^^ { Divide(_, _) })
  private def factor: Parser[Expression] =
    identifier ^^
      build(Reference) |
    number ^^
      build(Value) |
    !Symbol("(") ~~ expression ~~ !Symbol(")")

  private def condition: Parser[Condition] =
    !Keyword("ODD") ~~ expression ^^
      build(Odd) |
    expression ~~ !Symbol("=") ~~ expression ^^
      build(Equal) |
    expression ~~ !Symbol("<") ~~ expression ^^
      build(Less) |
    expression ~~ !Symbol(">") ~~ expression ^^
      build(Greater) |
    expression ~~ !Symbol("<>") ~~ expression ^^
      build(NotEqual) |
    expression ~~ !Symbol("<=") ~~ expression ^^
      build(LessOrEqual) |
    expression ~~ !Symbol(">=") ~~ expression ^^
      build(GreaterOrEqual)

  override protected def root: Parser[Syntax] = block ~~ !Symbol(".")
}